import { isFunction, isString, cloneDeep, isObject } from "./index";
import Vue from "vue";

/**
 * 解析 JSX 节点，并根据参数进行处理。
 * @param {Object} vnode - 虚拟节点对象。
 * @param {Object} options - 解析选项。
 * @param {Vue} options.inst - Vue 实例。
 * @param {*} options.scope - 作用域对象。
 * @param {string} options.prop - 属性名称。
 * @param {Array} [options.children] - 子节点数组。
 * @param {Object} [options.$scopedSlots] - 作用域插槽对象。
 * @returns {*} 解析后的节点或错误提示。
 */
const parse_jsx = (vnode, options = {}) => {
  const { inst, scope, prop, $scopedSlots, children = [] } = options;
  const h = inst.$createElement;

  if (!vnode || !vnode.name) {
    return new Error("Invalid vnode or vnode name is undefined.");
  }

  if (vnode.name.startsWith("slot-")) {
    const rn = $scopedSlots && $scopedSlots[vnode.name];
    if (rn) {
      return rn({ scope });
    } else {
      return new Error(`Slot "${vnode.name}" not found in $scopedSlots.`);
    }
  }

  if (vnode.render) {
    if (!inst.$root.$options.components[vnode.name]) {
      Vue.component(vnode.name, cloneDeep(vnode));
    }
    delete vnode.props; // 避免 props 提示问题
  }

  const keys = [
    "class",
    "style",
    "props",
    "attrs",
    "domProps",
    "on",
    "nativeOn",
    "directives",
    "scopedSlots",
    "slot",
    "key",
    "ref",
    "refInFor",
  ];

  let data = { ...vnode }; // 避免不必要的深拷贝

  for (let i in data) {
    if (!keys.includes(i)) {
      delete data[i];
    }
  }

  if (scope) {
    data.attrs = data.attrs || {};
    data.on = data.on || {};
    data.props = data.props || {};
    data.props.scope = scope;
    const onInput = data.on.input;
    data.attrs.value = scope[prop];
    data.on.input = (val) => {
      inst.$set(scope, prop, val);
      if (onInput) {
        onInput(val);
      }
    };
  }
  return h(vnode.name, data, children);
};

/**
 * 渲染虚拟节点。
 * @param {*} vnode - 虚拟节点（字符串、函数或对象）。
 * @param {Object} options - 渲染选项。
 * @param {Vue} [options.inst] - Vue 实例，若不传则使用调用者的 this。
 * @param {string} options.prop - 属性名称。
 * @param {*} options.scope - 作用域对象。
 * @param {Object} [options.$scopedSlots] - 作用域插槽对象。
 * @returns {*} 渲染后的节点。
 */
export function renderNode(vnode, options) {
  const { prop, scope, $scopedSlots } = options;
  const inst = options.inst || this;
  const h = inst.$createElement;

  if (!vnode) {
    return null;
  }

  if (isString(vnode)) {
    return parse_jsx({ name: vnode }, { inst, prop, scope, $scopedSlots });
  }

  if (isFunction(vnode)) {
    return vnode({ scope, h });
  }

  if (isObject(vnode)) {
    if (isObject(vnode.name)) {
      const component = vnode.name;
      const curComponent = Object.values(inst.$options.components).find(
        (item) => item === vnode
      );
      if (curComponent) {
        vnode.name = curComponent.name;
      } else {
        let componentName =
          component.name ||
          `component_${Math.random().toString(36).substring(2, 9)}`;
        inst.$options.components[componentName] = component;
        vnode.name = componentName;
      }
    }

    if (vnode.context) {
      return vnode;
    }

    if (vnode.name) {
      const keys = ["el-select", "el-radio-group", "el-checkbox-group"];
      if (keys.includes(vnode.name)) {
        let options = vnode.options;
        if (isFunction(options)) {
          options = options(scope);
        }
        if (!options) {
          return new Error(`Options for ${vnode.name} are undefined or null.`);
        }
        const children = (options || []).map((e, i) => {
          if (vnode.name === "el-select") {
            let label, value;
            if (isString(e)) {
              label = value = e;
            } else if (isObject(e)) {
              label = e.label;
              value = e.value;
            } else {
              return new Error(
                "Invalid el-select option format. Should be string or { label: string, value: any }"
              );
            }
            return (
              <el-option
                {...{
                  props: {
                    key: i,
                    label,
                    value,
                    ...e.props,
                  },
                }}
              />
            );
          } else if (vnode.name === "el-radio-group") {
            return (
              <el-radio
                {...{
                  props: {
                    key: i,
                    label: e.value,
                    ...e.props,
                  },
                }}
              >
                {e.label}
              </el-radio>
            );
          } else if (vnode.name === "el-checkbox-group") {
            if (typeof scope[prop] === "undefined") {
              inst.$set(scope, prop, []);
            }
            return (
              <el-checkbox
                {...{
                  props: {
                    key: i,
                    label: e.value,
                    ...e.props,
                  },
                }}
              >
                {e.label}
              </el-checkbox>
            );
          } else {
            return null;
          }
        });
        return parse_jsx(vnode, { inst, prop, scope, children });
      } else {
        return parse_jsx(vnode, { inst, prop, scope, $scopedSlots });
      }
    } else {
      return new Error("VNode name is undefined.");
    }
  }
}